
#include "stm32g4_sys.h"  

//系统变量
u32 SYS_task=0; //任务标志
u16 SYS_idle=0; //空闲计数
u16 SYS_idle_dis=0; //空闲计数的平均
vu32 SYS_time=0; //周期计数
void wait(vu32 i)
{
	while(i--);
}
int void_putchar(int c)
{
	return 0;
}
int (*IO_putchar)(int)=void_putchar;	//初始化的函数
int fputc(int c, FILE *f)
{
	return IO_putchar(c);
}
//设置向量表偏移地址
//NVIC_VectTab:基址
//Offset:偏移量		 
void MY_NVIC_SetVectorTable(u32 NVIC_VectTab,u32 Offset)	 
{ 	   	  
	SCB->VTOR=NVIC_VectTab|(Offset&(u32)0xFFFFFE00);//设置NVIC的向量表偏移寄存器,VTOR低9位保留,即[8:0]保留。
}
//设置NVIC分组
//NVIC_Group:NVIC分组 0~4 总共5组 		   
void MY_NVIC_PriorityGroupConfig(u8 NVIC_Group)	 
{ 
	u32 temp,temp1;	  
	temp1=(~NVIC_Group)&0x07;//取后三位
	temp1<<=8;
	temp=SCB->AIRCR;  //读取先前的设置
	temp&=0X0000F8FF; //清空先前分组
	temp|=0X05FA0000; //写入钥匙
	temp|=temp1;	   
	SCB->AIRCR=temp;  //设置分组
}
//设置NVIC 
//NVIC_PreemptionPriority:抢占优先级
//NVIC_SubPriority       :响应优先级
//NVIC_Channel           :中断编号
//NVIC_Group             :中断分组 0~4
//注意优先级不能超过设定的组的范围!否则会有意想不到的错误
//组划分:
//组0:0位抢占优先级,4位响应优先级
//组1:1位抢占优先级,3位响应优先级
//组2:2位抢占优先级,2位响应优先级
//组3:3位抢占优先级,1位响应优先级
//组4:4位抢占优先级,0位响应优先级
//NVIC_SubPriority和NVIC_PreemptionPriority的原则是,数值越小,越优先	   
void MY_NVIC_Init(u8 NVIC_PreemptionPriority,u8 NVIC_SubPriority,u8 NVIC_Channel,u8 NVIC_Group)	 
{ 
	u32 temp;	  
	MY_NVIC_PriorityGroupConfig(NVIC_Group);//设置分组
	temp=NVIC_PreemptionPriority<<(4-NVIC_Group);	  
	temp|=NVIC_SubPriority&(0x0f>>NVIC_Group);
	temp&=0xf;								//取低四位
	NVIC->ISER[NVIC_Channel/32]|=1<<NVIC_Channel%32;//使能中断位(要清除的话,设置ICER对应位为1即可)
	NVIC->IP[NVIC_Channel]|=temp<<4;				//设置响应优先级和抢断优先级   	    	  				   
} 
//外部中断配置函数
//只针对GPIOA~I;不包括PVD,RTC,USB_OTG,USB_HS,以太网唤醒等
//参数:
//GPIOx:0~8,代表GPIOA~I
//BITx:需要使能的位;
//TRIM:触发模式,1,下升沿;2,上降沿;3，任意电平触发
//该函数一次只能配置1个IO口,多个IO口,需多次调用
//该函数会自动开启对应中断,以及屏蔽线   	    
void Ex_NVIC_Config(u8 GPIOx,u8 BITx,u8 TRIM) 
{ 
	u8 EXTOFFSET=(BITx%4)*4;  
	RCC->APB2ENR|=RCC_APB2ENR_SYSCFGEN;			//使能SYSCFG时钟  
	SYSCFG->EXTICR[BITx/4]&=~(0x000F<<EXTOFFSET);//清除原来设置！！！
	SYSCFG->EXTICR[BITx/4]|=GPIOx<<EXTOFFSET;	//EXTI.BITx映射到GPIOx.BITx 
	//自动设置
	EXTI->IMR1|=1<<BITx;					//开启line BITx上的中断(如果要禁止中断，则反操作即可)
	if(TRIM&0x01)EXTI->FTSR1|=1<<BITx;	//line BITx上事件下降沿触发
	if(TRIM&0x02)EXTI->RTSR1|=1<<BITx;	//line BITx上事件上升降沿触发
}
//把所有时钟寄存器复位
void MYRCC_DeInit(void)
{
	RCC->CR |= (1<<8); // Set HSION bit
	while((RCC->CR & (1<<10))==0); //等HSI16稳定
	RCC->CR = (1<<8); //清除
	RCC->CR = (1<<8); //清除两遍
	RCC->CFGR = 0x00000005; // Reset CFGR register
	RCC->PLLCFGR = 0x1000;
	RCC->CIER=0;
	RCC->CICR=0xffffffff;
	RCC->CSR|=(1<<23);
}
//GPIO复用设置
//GPIOx:GPIOA~GPIOI.
//BITx:0~15,代表IO引脚编号.
//AFx:0~15,代表AF0~AF15.
//AF0~15设置情况(这里仅是列出常用的,详细的请见407数据手册,56页Table 7):
void GPIO_AF_Set(GPIO_TypeDef* GPIOx,u8 BITx,u8 AFx)
{  
	GPIOx->AFR[BITx>>3]&=~(0X0F<<((BITx&0X07)*4));
	GPIOx->AFR[BITx>>3]|=(u32)AFx<<((BITx&0X07)*4);
}   
//GPIO通用设置 
//GPIOx:GPIOA~GPIOI.
//BITx:0X0000~0XFFFF,位设置,每个位代表一个IO,第0位代表Px0,第1位代表Px1,依次类推.比如0X0101,代表同时设置Px0和Px8.
//MODE:0~3;模式选择,0,输入(系统复位默认状态);1,普通输出;2,复用功能;3,模拟输入.
//OTYPE:0/1;输出类型选择,0,推挽输出;1,开漏输出.
//OSPEED:0~3;输出速度设置,0,2Mhz;1,25Mhz;2,50Mhz;3,100Mh. 
//PUPD:0~3:上下拉设置,0,不带上下拉;1,上拉;2,下拉;3,保留.
//注意:在输入模式(普通输入/模拟输入)下,OTYPE和OSPEED参数无效!!
void GPIO_Set(GPIO_TypeDef* GPIOx,u32 BITx,u32 MODE,u32 OTYPE,u32 OSPEED,u32 PUPD)
{  
	u32 pinpos=0,pos=0,curpin=0;
	for(pinpos=0;pinpos<16;pinpos++)
	{
		pos=1<<pinpos;	//一个个位检查 
		curpin=BITx&pos;//检查引脚是否要设置
		if(curpin==pos)	//需要设置
		{
			GPIOx->MODER&=~(3<<(pinpos*2));	//先清除原来的设置
			GPIOx->MODER|=MODE<<(pinpos*2);	//设置新的模式 
			if((MODE==0X01)||(MODE==0X02))	//如果是输出模式/复用功能模式
			{  
				GPIOx->OSPEEDR&=~(3<<(pinpos*2));	//清除原来的设置
				GPIOx->OSPEEDR|=(OSPEED<<(pinpos*2));//设置新的速度值  
				GPIOx->OTYPER&=~(1<<pinpos) ;		//清除原来的设置
				GPIOx->OTYPER|=OTYPE<<pinpos;		//设置新的输出模式
			}  
			GPIOx->PUPDR&=~(3<<(pinpos*2));	//先清除原来的设置
			GPIOx->PUPDR|=PUPD<<(pinpos*2);	//设置新的上下拉
		}
	}
} 
//看门狗开启
//定时器时钟为32k,计数4096，122ms
void wtd_ini(u8 div) //按默认0,488ms
{
	DBGMCU->CR |= (1<<8); //DBG_IWDG_STOP调试时看门狗不工作
	IWDG->KR=0x5555;
	IWDG->PR=div;//预分频:4/8/16/32……,写之前必须先关写保护
	IWDG->KR=0x5555;
	IWDG->RLR=4000/10;//重装载值12bit
	IWDG->KR=0xcccc;//启动看门狗	
}
void wtd_tri(void)
{
	IWDG->KR=0xaaaa;
}
//THUMB指令不支持汇编内联
//采用如下方法实现执行汇编指令WFI  
void WFI_SET(void)
{
	__ASM volatile("wfi");
}
void switch_2_addr(u32 *pc) //切换到指定的程序指针
{
	u32 i = 0;
	MYRCC_DeInit();

	NVIC->ICER[0] = 0xFFFFFFFF;
	NVIC->ICER[1] = 0x000007FF;
	NVIC->ICPR[0] = 0xFFFFFFFF;
	NVIC->ICPR[1] = 0x000007FF;
	for(i = 0; i < 0x0B; i++)
	{
		NVIC->IP[i] = 0x00000000;
	}
	OS_CLOSE_INT;

	__set_MSP(*pc);//堆栈指针是应用程序起始的第一个u32
	((void (*)(void))*(pc+1))();//入口函数是应用程序起始的第二个u32
}
//进入待机模式	  
void Sys_Standby(void)
{ 
	//return;
	SCB->SCR|=1<<2;		//使能SLEEPDEEP位 (SYS->CTRL)
	RCC->APB1ENR1|=RCC_APB1ENR1_PWREN;//使能电源时钟 
	PWR->CR3|=(1<<8) |  //sram2
			(1<<10) | //APC，睡眠时引脚上下拉
			(1<<15); //设置WKUP用于唤醒
	PWR->CR1|=3;  //standby
	PWR->SCR|=0x1f;      //清除Wake-up 标志
	WFI_SET();			//执行WFI指令,进入待机模式
}
void Sys_Shutdown(void)
{ 
	//return;
	SCB->SCR|=1<<2;		//使能SLEEPDEEP位 (SYS->CTRL)
	RCC->APB1ENR1|=RCC_APB1ENR1_PWREN;//使能电源时钟 
	PWR->CR3|=//(1<<8) |  //sram2
			//(1<<10) | //APC，睡眠时引脚上下拉
			(1<<15); //设置WKUP用于唤醒
	PWR->CR1|=4;  //shutdown
	PWR->SCR|=0x1f;      //清除Wake-up 标志
	WFI_SET();			//执行WFI指令,进入待机模式
}
//系统软复位   
void Sys_Soft_Reset(void)
{   
	SCB->AIRCR =0X05FA0000|(u32)0x04;
}
void Stm32_Clock_Init(u32 plln,u32 pllm,u32 pllp,u32 pllq) //开机配置
{
	u16 retry=0;
	u8 status=0;
	pllm-=1; //PLLM为1，需要写0
//1、在SystemInit中只设置了FPU，这个在s文件中做
//2、flash配置，按默认
	// - Prefetch disabled
	// - Instruction cache enabled
	// - Data cache enabled
//3、配置电源
	PWR->CR5=0;	//高性能模式,range 1 boost,认为现在是range 1 normal
//4、开时钟
	RCC->CR|=1<<16;				//HSE 开启
	while(((RCC->CR&(1<<17))==0)&&(retry<0X1FFF))retry++;//等待HSE RDY
	if(retry==0X1FFF)status=1;	//HSE无法就绪
	else   
	{
		//Initializes the CPU, AHB and APB busses clocks 
		RCC->CR&=~(1<<24);	//关闭主PLL
		RCC->PLLCFGR=3 | //时钟源HSE
			(pllm<<4) | 
			(plln<<8) |
			(pllp<<27) | //P分频
			((pllq/2-1)<<21) | //Q分频
			(0<<25); //R分频
		RCC->CR|=1<<24;			//打开主PLL
		RCC->PLLCFGR |= (1<<16) | (1<<24); //P输出使能,R输出使能
		while((RCC->CR&(1<<25))==0);//等待PLL准备好 
		//配置flash
		FLASH->ACR|=1<<8;		//指令预取使能.
		FLASH->ACR|=1<<9;		//指令cache使能.
		FLASH->ACR|=1<<10;		//数据cache使能.
		FLASH->ACR|=4<<0;		//5个CPU周期4个等待周期. 
		RCC->CFGR&=~(3<<0);		//清零
		RCC->CFGR|=3<<0;		//选择主PLL作为系统时钟	 
		while((RCC->CFGR&(3<<2))!=(3<<2));//等待主PLL作为系统时钟成功. 
		RCC->CFGR|=(0<<4)|(0<<8)|(0<<11);//HCLK 不分频;APB1 不分频;APB2 不分频. 
	} 
	//配置向量表				  
#ifdef  VECT_TAB_RAM
	MY_NVIC_SetVectorTable(1<<29,0x0);
#else   
	MY_NVIC_SetVectorTable(0,0x0);
#endif 
}

//DBANK=1时，2个bank，每个256KB，每个扇区2KB
//DBANK=0时，1个bank，每个512KB，每个扇区4KB
#pragma pack(1)
typedef struct
{
	u32 ACR;
	u32 PDKEYR;
	u32 KEYR;
	u32 OPTKEYR;
	//u32 SR;
	u8 EOP:1; //操作成功（中断使能时）
	u8 OPERR:1; //操作失败（中断使能时）
	u8 res:1;
	u8 PROGERR:1; //写之前不是0xffffffff（写1清零）
	u8 WRPERR:1; //写保护（写1清零）
	u8 PGAERR:1; //失败（写1清零）
	u8 SIZERR:1; //失败（写1清零）
	u8 PGSERR:1; //失败（写1清零）

	u8 MISSERR:1; //失败（写1清零）
	u8 FASTERR:1; //失败（写1清零）
	u8 res1:4;
	u8 RDERR:1; //失败（写1清零）
	u8 OPTVERR:1; //失败（写1清零）
	u16 bsy:1; //忙
	u16 res2:15;
	//u32 CR;
	u32 PG:1; //MFLASH programming activated.
	u32 PER:1; //page Erase 
	u32 MER1:1; //Erase activated for bank1
	u32 PNB:7; //页号
	u32 res3:1; 
	u32 BKER:1; //bit11
	u32 res4:3; // 
	u32 MER2:1; //Erase activated for bank2   bit15
	u32 STRT:1; //开始擦除
	u32 OPTSTRT:1; //triggers an options operation 
	u32 FSTPG:1; //Fast programming
	u32 res5:5;
	u32 EOPIE:1; //This bit enables the interrupt generation when the EOP bit in the FLASH_SR register goes to 1
	u32 ERRIE:1; //This bit enables the interrupt generation when the OPERR bit in the FLASH_SR register is set to 1.
	u32 res6:5;
	u32 LOCK:1; //Write to 1 only
	//u32
	u32 ECCR;
	u32 RESERVED1;
	u32 OPTR;
} MFLASH_TypeDef;
#pragma pack()
#define MFLASH               ((volatile MFLASH_TypeDef *) FLASH_R_BASE)
int flash_erase(void *pf) //擦FLASH扇区,返回本扇区大小
{
	u32 FLASH_PAGE=0x1000/2; //单区模式，4KB扇区，默认双区，2KB
	u32 t=(u32)pf;
	if(0x08000000>t || t>(0x08000000+KB(512))) return 0;
	//解锁：
	MFLASH->KEYR=0x45670123;
	MFLASH->KEYR=0xCDEF89AB;
	while(MFLASH->bsy);
	MFLASH->PER=1;
	//选择扇区
	t-=0x08000000;
	t/=FLASH_PAGE;
	MFLASH->PNB=t;
	
	MFLASH->STRT=1;
	while(MFLASH->bsy);
	MFLASH->PER=0;
	//上锁
	MFLASH->LOCK=1; 
	return FLASH_PAGE;
}
int flash_write(u8 *p,int n,void *addr) //直接写入,n为字节数,会溢出
{
	int i,t=(n+7)/8; //t为写操作的次数
	//解锁：
	MFLASH->KEYR=0x45670123;
	MFLASH->KEYR=0xCDEF89AB;

	while(MFLASH->bsy);
	MFLASH->PG=1;
	for(i = 0; i < t; i++) //i是写入的次数
	{
		int j=i*2;
		((u32*)addr)[j]=((u32*)p)[j];
		j++;
		((u32*)addr)[j]=((u32*)p)[j];
		while(MFLASH->bsy);
	}
	MFLASH->PG=0;
	//上锁
	MFLASH->LOCK=1; 
	return 0;
}
int flash_eraseAwrite(u8 *p,int n,void *pf) //擦除并写入,输入数据指针，数据长度(单位字节)，FLASH地址
{
	int i;
	flash_erase(pf); //擦除
	flash_write(p,n,pf); //写入
	for(i = 0; i < n; i++) //校验
	{
		if(p[i]!=((u8*)pf)[i]) return 1;
	}
	return 0;
}

